﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.ComponentModel;
using FinPlusAssembler;
using FinPlusInterfaces;
using FinPlusCompCore;

namespace FinPlusCompProxy
{
    public class BondCurve : ConcurrentDictionary<string, IRate>
    {
        private enum Params { LevelUpdate, Refresh, CurveName, Shock, Instrument, NumZCM, NumBond, DayCount, Tolerance, Holidays };
        private enum BuildType { ZCMBuild, FixedRateBondBuild, Unknown };

        private IList<IPod> _instruments;
        private FinPlusLevel _level;
        private QLAnalytics _analytics;
        private IPods _instrumentBuilds;
        private IPod _bondCurveBuild;
        private BuildEvent _buildEvents;
        private int _numZcm, _numBond, _zcmCount, _bondCount;
        private string _curveName, _dayCount, _holidays;
        private double _tolerance;

        public BondCurve(QLAnalytics analytics, string curveName, IPod bondCurveBuild, IPods instrumentBuilds, IList<IPod> instruments, BuildEvent buildEvents)
        {
            _analytics = analytics;
            _curveName = curveName;
            _bondCurveBuild = bondCurveBuild;
            _instrumentBuilds = instrumentBuilds;
            _instruments = instruments;
            _level = new FinPlusLevel(analytics.BuySell);
            _dayCount = _bondCurveBuild[Params.DayCount.ToString()].ToString();
            _tolerance = double.Parse(_bondCurveBuild[Params.Tolerance.ToString()].ToString());
            _holidays = _bondCurveBuild[Params.Holidays.ToString()].ToString();
            _numZcm = (int)_bondCurveBuild.Value(Params.NumZCM.ToString());
            _numBond = (int)_bondCurveBuild.Value(Params.NumBond.ToString());
            _buildEvents = buildEvents;
            Initialise();
        }

        public void Initialise()
        {
            foreach (var instrument in _instruments)
            {
                if (_buildEvents == BuildEvent.Update || _buildEvents == BuildEvent.UpdateAndRefresh)
                    instrument.Get(Params.LevelUpdate.ToString(), IOC.New<IPod>(), Curve_Update);

                if (_buildEvents == BuildEvent.Refresh || _buildEvents == BuildEvent.UpdateAndRefresh)
                    instrument.Get(Params.Refresh.ToString(), string.Empty, Curve_Refresh);

                instrument.Get(Params.Shock.ToString(), Curve_Shock);

                var instrumentName = instrument[Params.Instrument.ToString()].ToString();
                var instrumentBuild = _instrumentBuilds.First("Name='Instrument' And Value='" + instrumentName + "'");

                if (instrumentBuild != null)
                {
                    this[instrumentName] = AddRate(instrumentName, instrument, instrumentBuild);
                    instrument.Get(Params.Refresh.ToString(), string.Empty).Update(instrumentName);
                }
            }
        }

        //private
        private IRate AddRate(string instrumentName, IPod instrument, IPod instrumentBuild)
        {
            switch (instrumentBuild[PodField.Type.ToString()].ToString().EnumParse<BuildType>(BuildType.Unknown))
            {
                case BuildType.ZCMBuild: _zcmCount++; return new ZCMRate(_analytics, instrumentName, instrument, instrumentBuild);
                case BuildType.FixedRateBondBuild: _bondCount++; return new BondRate(_analytics, instrumentName, instrument, instrumentBuild);
                case BuildType.Unknown: break; //TODO 
            }

            return null;
        }

        private void BuildCurve()
        {
            lock (_analytics.QL)
                _analytics.QL.BondCurve(_analytics.MarketName, _curveName, _analytics.DiscountCurve, _analytics.AsOf, Rates(_analytics.BuySell), _dayCount, _tolerance, _holidays);
        }

        private string Rates(string buySell)
        {
            return string.Join(",", this.Values.Select(f => f.Build(buySell)).ToArray<string>());
        }

        private bool IsValid()
        {
            if (_numZcm != _zcmCount || _numBond != _bondCount)
                return false;

            foreach (var rate in this.Values)
                if (Double.IsNaN(rate.Value))
                    return false;

            return true;
        }

        //events
        private void Curve_Update(object s, PropertyChangedEventArgs a)
        {
            var level = (IPod)((IBean)s).Object;
            var instrument = level[Params.Instrument.ToString()].ToString();

            if (this[instrument].Update(level) && IsValid())
                BuildCurve();
        }

        private void Curve_Refresh(object s, PropertyChangedEventArgs a)
        {
            if (IsValid() && (((IBean)s).ToString().Equals(_curveName)))
                BuildCurve();
        }

        private void Curve_Shock(object s, PropertyChangedEventArgs a)
        {
            var shock = (IPod)((IBean)s).Object;
            var curveName = shock[Params.CurveName.ToString()].ToString();
            if (curveName.Equals(_curveName))
            {
                var instrumentName = shock[Params.Instrument.ToString()].ToString();
                var value = (int)shock.Get(Params.Shock.ToString()).Object;
                this[instrumentName].Shock = value;
                if (value != 0 && IsValid()) BuildCurve();
            }
        }
    }


}
